﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System.Threading;
using MvdDonk.Bomberman.Entities;
using System.Net;
using MvdDonk.Bomberman.Network.Events;
using MvdDonk.Bomberman.Network.Messages;

namespace MvdDonk.Bomberman.Network
{
    public class Client
    {
        private NetClient client;
        private string clientName;
        private int serverPort;
        private int syncServerIntervalInMiliseconds = 200;
        private TimeSpan syncServerTimer;

        private int intervalInMiliseconds = 50;
        private TimeSpan serverTimer;

        public delegate void MovementUpdateEventHandler(object sender, MovementUpdateEventArgs e);
        public event MovementUpdateEventHandler MovementUpdate;

        public delegate void PositionSyncEventHandler(object sender, PositionSyncEventArgs e);
        public event PositionSyncEventHandler PositionSync;

        public delegate void ServerFoundEventHandler(object sender, ServerFoundEventArgs e);
        public event ServerFoundEventHandler ServerFound;

        public bool IsConnected
        {
            get
            {
                return (client != null && client.ConnectionStatus == NetConnectionStatus.Connected);
            }
        }

        public Client(string appIdentifier, int serverPort, string clientName)
        {
            this.clientName = clientName;
            Console.WriteLine("Initializing client");
            this.serverPort = serverPort;
            var config = new NetPeerConfiguration(appIdentifier);
            config.EnableMessageType(NetIncomingMessageType.DiscoveryResponse);

            client = new NetClient(config);
            Console.WriteLine("Starting client...");
            client.Start();
        }

        public long NetworkId { get { return client.UniqueIdentifier; } }

        public void FindServers()
        {
            Console.WriteLine("Looking for servers");
            client.DiscoverLocalPeers(serverPort);
        }

        public void Connect(IPEndPoint endPoint)
        {
            client.Connect(endPoint, client.CreateMessage(clientName));
        }

        public void Disconnect()
        {
            client.Disconnect("Bye later!");
        }

        public void Poll()
        {
            // read messages
            NetIncomingMessage msg;
            while ((msg = client.ReadMessage()) != null)
            {
                switch (msg.MessageType)
                {
                    case NetIncomingMessageType.DiscoveryResponse:
                        Console.WriteLine("Found a server on: " + msg.SenderEndpoint.Address + ":" + msg.SenderEndpoint.Port);
                        ServerFound(this, new ServerFoundEventArgs(msg.ReadString(), msg.SenderEndpoint));
                        break;
                    case NetIncomingMessageType.Data:
                        //Console.WriteLine("Received data from server");
                        // server sent a position update
                        var receivedMsg = MessageFinder.GetMessageFromServer(msg);
                        if (receivedMsg is MovementMessage)
                        {
                            var typed = receivedMsg as MovementMessage;
                            if (MovementUpdate != null)
                                MovementUpdate(this, new MovementUpdateEventArgs(typed.MovementState, typed.MovementVector, receivedMsg.NetworkId, client.UniqueIdentifier == typed.NetworkId));
                        }
                        if (receivedMsg is PositionMessage)
                        {
                            var typed = receivedMsg as PositionMessage;
                            if (PositionSync != null)
                                PositionSync(this, new PositionSyncEventArgs(typed.Position, receivedMsg.NetworkId, client.UniqueIdentifier == typed.NetworkId));
                        }
                        break;
                    case NetIncomingMessageType.StatusChanged:
                        Console.WriteLine("New status: " + client.Status + " (Reason: " + msg.ReadString() + ")");
                        break;
                }
            }
        }

        public void UpdateMovement(PlayerState movementState, Vector2 movementVector)
        {
            var msg = new MovementMessage(movementVector, movementState);
            msg.SendToServer(client);
        }

        public void SyncPosition(Vector2 position)
        {
            var msg = new PositionMessage(position);
            msg.SendToServer(client);
        }
    }
}
